Linux SOcket CAN 学习笔记

您所在的位置:网站首页 can 错误帧 Linux SOcket CAN 学习笔记

Linux SOcket CAN 学习笔记

2024-07-13 11:21| 来源: 网络整理| 查看: 265

要实现同一网络节点上的应用程序之间能相互交换数据,如果CAN网络的硬件不支持回环功能,一种低效的方案是使用Socket CAN核心部分来实现软件回环CAN错误帧的详细格式定义在linux头文件中:include/linux/can/error.h可过滤后传给用户:当一个物理层或者MAC层的错误被(CAN控制器)检测到之后,驱动创建一个相应的错误帧。错误帧可以被应用程序通过CAN的过滤机制请求得到。过滤机制允许选择需要的错误帧的类型。默认情况下,接收错误帧的功能是禁止的。有两个CAN的协议可以选择,一个是原始套接字协议( raw socket protocol),另一个是广播管理协议BCM(broadcast manager)。你可以这样来打开一个套接字: s = socket(PF_CAN, SOCK_RAW, CAN_RAW); 或者 s = socket(PF_CAN, SOCK_DGRAM, CAN_BCM); 在成功创建一个套接字之后,你通常需要使用bind(2)函数将套接字绑定在某个CAN接口上(这和TCP/IP使用不同的IP地址不同,参见第3章)。在绑定 (CAN_RAW)或连接(CAN_BCM)套接字之后,你可以在套接字上使用read(2)/write(2),也可以使用send(2)/sendto(2)/sendmsg(2)和对应的recv*操作。当然也会有CAN特有的套接字选项,下面将会说明。

基本的CAN帧结构体和套接字地址结构体定义在include/linux/can.h:

/* * 扩展格式识别符由 29 位组成。其格式包含两个部分:11 位基本 ID、18 位扩展 ID。 Controller Area Network Identifier structure * - bit 0-28 : CAN识别符 (11/29 bit) - bit 29 : 错误帧标志 (0 = data frame, 1 = error frame) - bit 30 : 远程发送请求标志 (1 = rtr frame) - bit 31 :帧格式标志 (0 = standard 11 bit, 1 = extended 29 bit) */ typedef __u32 canid_t; struct can_frame { canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ __u8 can_dlc; /* 数据长度: 0 .. 8 */ __u8 data[8] __attribute__((aligned(8))); }; 结构体的有效数据在data[]数组中,它的字节对齐是64bit的,所以用户可以比较方便的在data[]中传输自己定义的结构体和共用体。CAN总线中没有默认的字节序。在CAN_RAW套接字上调用read(2),返回给用户空间的数据是一个struct can_frame结构体。

就像PF_PACKET套接字一样,sockaddr_can结构体也有接口的索引,这个索引绑定了特定接口:

struct sockaddr_can { sa_family_t can_family; int can_ifindex; union { /* transport protocol class address info (e.g. ISOTP) */ struct { canid_t rx_id, tx_id; } tp; /* reserved for future CAN protocols address information */ } can_addr; }; 指定接口索引需要调用ioctl()(比如对于没有错误检查CAN_RAW套接字): int s; struct sockaddr_can addr; struct ifreq ifr; s = socket(PF_CAN, SOCK_RAW, CAN_RAW); strcpy(ifr.ifr_name, "can0" ); ioctl(s, SIOCGIFINDEX, &ifr); addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; bind(s, (struct sockaddr *)&addr, sizeof(addr)); (..) 为了将套接字和所有的CAN接口绑定,接口索引必须是0。这样套接字便可以从所有使能的CAN接口接收CAN帧。recvfrom(2)可以指定从哪个接口接收。在一个已经和所有CAN接口绑定的套接字上,sendto(2)可以指定从哪个接口发送。

从一个CAN_RAW套接字上读取CAN帧也就是读取struct can_frame结构体:

struct can_frame frame; nbytes = read(s, &frame, sizeof(struct can_frame)); if (nbytes < 0) { perror("can raw socket read"); return 1; } /* paranoid check ... */ if (nbytes < sizeof(struct can_frame)) { fprintf(stderr, "read: incomplete CAN frame\n"); return 1; } /* do something with the received CAN frame */

写CAN帧也是类似的,需要用到write (2)函数:

nbytes = write(s, &frame, sizeof(struct can_frame)); 如果套接字跟所有的CAN接口都绑定了(addr.can_index = 0),推荐使用recvfrom(2)获取数据源接口的信息: struct sockaddr_can addr; struct ifreq ifr; socklen_t len = sizeof(addr); struct can_frame frame; nbytes = recvfrom(s, &frame, sizeof(struct can_frame), 0, (struct sockaddr*)&addr, &len); /* get interface name of the received CAN frame */ ifr.ifr_ifindex = addr.can_ifindex; ioctl(s, SIOCGIFNAME, &ifr); printf("Received a CAN frame from interface %s", ifr.ifr_name); 对于绑定了所有接口的套接字,向某个端口发送数据必须指定接口的详细信息: strcpy(ifr.ifr_name, "can0"); ioctl(s, SIOCGIFINDEX, &ifr); addr.can_ifindex = ifr.ifr_ifindex; addr.can_family = AF_CAN; nbytes = sendto(s, &frame, sizeof(struct can_frame), 0, (struct sockaddr*)&addr, sizeof(addr));

CAN_RAW套接字的用法和CAN字符设备的用法是类似的。为了使用CAN套接字的新特性,在绑定原始套接字的时候将会默认开启以下特性:

filter将会接收所有的数据套接字仅仅接收有效的数据帧(=> no error frames)发送帧的回环功能被开启(参见 3.2节)(回环模式下)套接字不接收它自己发送的帧

CAN_RAW套接字的接收可以使用CAN_RAW_FILTER套接字选项指定的多个过滤规则(过滤器)来过滤。

过滤规则(过滤器)的定义在 include/linux/can.h中:

struct can_filter { canid_t can_id; canid_t can_mask; };

过滤规则的匹配:

& mask == can_id & mask /* #define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */ #define CAN_ERR_FLAG 0x20000000U /* error frame */ */

这和大家熟知的CAN控制器硬件过滤非常相似。可以使用 CAN_INV_FILTER这个宏将can_filter结构体的成员can_id中的比特位反转。和CAN控制器的硬件过滤形成鲜明对比的是,用户可以为每一个打开的套接字设置多个独立的过滤规则(过滤器):

/* /* valid bits in CAN ID for frame formats */ #define CAN_SFF_MASK 0x000007FFU /* 标准帧格式 (SFF) */ #define CAN_EFF_MASK 0x1FFFFFFFU /* 扩展帧格式 (EFF) */ #define CAN_ERR_MASK 0x1FFFFFFFU /* 忽略EFF, RTR, ERR标志 */ */ struct can_filter rfilter[2]; rfilter[0].can_id = 0x123; rfilter[0].can_mask = CAN_SFF_MASK; rfilter[1].can_id = 0x200; rfilter[1].can_mask = 0x700; setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));

为了在指定的CAN_RAW套接字上禁用接收过滤规则,可以这样:

setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);

在一些极端情况下不需要读取数据,可以把过滤规则清零(所有成员设为0),这样原始套接字就会忽略接收到的CAN帧。在这种仅仅发送数据(不读取)的应用中可以在内核中省略接收队列,以此减少CPU的负载(虽然只能减少一点点)。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3